﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using GameStateManagement.Model.Entities;

namespace GameStateManagement.Model.Physics
{
    public abstract class PhysicsModule
    {
        #region Fields

        protected List<PhysicsForce> GlobalForces;
        protected List<ForceGenerator> ForceGenerators;

        #endregion

        #region Properties

        public List<PhysicsForceField> ForceFields
        {
            get { return _forceFields; }
            protected set { _forceFields = value; }
        }
        protected List<PhysicsForceField> _forceFields;

        #endregion

        #region Events
        public delegate void CollisionOccuredEventHandler(object sender, EventArgs e);

        /// <summary>
        /// Event gets fired when game changes state.
        /// </summary>
        public event CollisionOccuredEventHandler CollisionOccured;
        protected virtual void OnCollisionOccured(CollisionEventArgs e)
        {
            if (CollisionOccured != null)
                CollisionOccured(this, e);
        }


        #endregion

        #region Public Methods

        public PhysicsForceField[] GetForceFields()
        {
            return _forceFields.ToArray();
        }

        public PhysicsForce[] GetGlobalForces()
        {
            return GlobalForces.ToArray();
        }

        /// <summary>
        /// Apply the resultant to all entities.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="entities"></param>
        public abstract void ApplyForcesOnEntites(GameTime gameTime, CompositeEntity entities);

        /// <summary>
        /// Check if entity is affected by a forcefield and returns the resultant of the force.
        /// </summary>
        /// <param name="entity">entity to check.</param>
        /// <returns>The resulting force of the fields.</returns>
        protected abstract Vector2 CheckForceFields(MovingEntity entity);

        /// <summary>
        /// Updates forces and calculates them to a single vector.
        /// </summary>
        protected abstract void UpdateResultant();

        public void UpdateForces(GameTime gameTime)
        {
            foreach (PhysicsForce force in GlobalForces)
                force.Update(gameTime);
        }

        /// <summary>
        /// Performs a check if any entities have collided, and updates the affected parameters of the entity.
        /// </summary>
        /// <param name="entities">The entities to check collisions on.</param>
        protected abstract void CheckCollisionsOnEntities(CompositeEntity entities);

        /// <summary>
        /// Adds a force to the list.
        /// </summary>
        /// <param name="force"></param>
        public void AddGlobalForce(PhysicsForce force)
        {
            GlobalForces.Add(force);
        }

        public void AddForceField(PhysicsForceField forceField)
        {
            _forceFields.Add(forceField);
        }

        /// <summary>
        /// Clear all forces.
        /// </summary>
        public void ClearForces()
        {
            GlobalForces.Clear();
        }
        #endregion

        #region Lifetime methods
        
        /*
        public virtual void LoadContent(GameModel game)
        {
            foreach (PhysicsForceField field in _forceFields)
                field.LoadContent(game);
        }
        */

        #endregion
    }
}
